package com.glsc.ngateway.ldap.service.smartbi;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.glsc.ngateway.common.api.ldap.dto.LdapEhrEmpDto;
import com.glsc.ngateway.common.base.dto.opmanage.report.BiExportDto;
import com.glsc.ngateway.common.base.enums.OaHrmresourceStatusEnum;
import com.glsc.ngateway.ldap.service.LdapEhrEmpService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import smartbi.sdk.ClientConnector;
import smartbi.sdk.service.simplereport.Parameter;
import smartbi.sdk.service.spreadsheetreport.SSReport;
import smartbi.sdk.service.user.UserManagerService;
import smartbi.user.IDepartment;
import smartbi.user.IRole;
import smartbi.user.IUser;

import javax.annotation.Resource;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.validation.constraints.NotNull;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Component
public class SmartBiUserManager {

    Logger logger = LoggerFactory.getLogger(SmartBiUserManager.class);

    private final String defaultDepartmentName = "glsc";
    private final String defaultRole = "glsc_web";
    private final String defaultPassword = "glsc601456";

    @Value("${smartbi.url}")
    String smartbi_url = "";

    @Value("${smartbi.username}")
    String smartbi_username = "";

    @Value("${smartbi.password}")
    String smartbi_password = "";

    @Value("${smartbi.key}")
    String smartbi_key = "";

    @Value("${smartbiv10.url}")
    String smartbiv10_url = "";

    @Value("${smartbiv10.username}")
    String smartbiv10_username = "";

    @Value("${smartbiv10.password}")
    String smartbiv10_password = "";

    @Value("${smartbiv10.key}")
    String smartbiv10_key = "";

    @Value("${nas.path.zyfile}")
    String nasPath = "";

    public static final List<String> IGNORE_ACCOUNT_LIST
            = Collections.synchronizedList(Arrays.asList("admin", "sysadmin", "itadmin"));

    @Resource
    private LdapEhrEmpService ldapEhrEmpService;

    /**
     * 根据用户名提供BI系统的token
     *
     * @param userAccount
     * @return
     */
    public String getToken(@NotNull String userAccount, String biVersion) {
        //增加用户合法性检查
        userAccount = userAccount.toLowerCase();
        if (IGNORE_ACCOUNT_LIST.contains(userAccount)) {
            logger.warn("账号{}，禁止登录BI", userAccount);
            return null;
        }

        LdapEhrEmpDto ldapEhrEmpDto = ldapEhrEmpService.findByBadge(userAccount);
        if (ldapEhrEmpDto != null && OaHrmresourceStatusEnum.isAlive(ldapEhrEmpDto.getStatus())) {
            logger.warn("账号{}，非OA合法账号，不允许登录", userAccount);
            return null;
        }

        //准备生成BI的token

        long time = System.currentTimeMillis();
        String timestr =DateUtil.date(time).toString("yyyy-MM-dd HH:mm:ss");//当前时间字符串，格式：yyyy-MM-dd HH:mm:ss

        String key = smartbi_key;
        if( Objects.nonNull(biVersion) && biVersion.equalsIgnoreCase(SmartBiVersionConstants.V10.getValue())) {
            key = smartbiv10_key;
        }

        String token = "";
        try {
            JSONObject json = new JSONObject();
            json.put("username", userAccount);
            json.put("datetime", time);
            token = URLEncoder.encode(getEncryptedString(json.toString(), key), "UTF-8");
        } catch (Exception e) {
            logger.error("用户:{},时间:{}密钥:{},生成token失败:{}", userAccount, timestr, key, e);
        }

        logger.info("用户:{},时间:{}密钥:{},生成token成功:{}", userAccount, timestr, key, token);

        return token;
    }


    /**
     * 将用户同步至BI系统。
     * 1) 如果该用户不存在，新建
     * 2) 如果该用户已经存在，不做任何操作
     * <p>
     * 目前所有的用户都属于${defaultDepartmentName}组，都属于${defaultRole}角色。
     * <p>
     * ${defaultRole}角色包含:
     * 1) 所有[数据集-galm]的引用权限和所有[分析报表-glam]的引用和查看权限。
     * 2) 电子表格的导出和打印权限。
     * <p>
     * 所有用户统一密码，且和glam的密码不一致，防止用户直接登陆BI系统，看到所有报表。
     *
     * @param
     * @return 没有同步成功额用户名列表
     */

    public String createUser(String userName) {
        ClientConnector conn = new ClientConnector(smartbi_url);
        try {
            logger.info("开始连接BI,用户名:{},密码{}", smartbi_username, smartbi_password);
            boolean con = conn.open(smartbi_username, smartbi_password);
            if (con) {
                logger.info("连接BI成功");
                UserManagerService userManagerService = new UserManagerService(conn);
                //{"departmentCode":"glam","name":"glam","alias":"glam","id":"Iff8080810176dfe6dfe65fb90176efdd74621699","desc":""}
                IDepartment glamDep = userManagerService.getDepartmentByName(defaultDepartmentName);
                if (glamDep == null) {
                    logger.error("所有BI用户创建失败,组{}不存在", defaultDepartmentName);
                }

                //key:username value:中文名

                Map<String, String> biUserMap = userManagerService.getAllUsers()
                        .stream().collect(Collectors.toMap(IUser::getName, IUser::getAlias));

                try {
                    //已经存在
                    if (biUserMap.containsKey(userName)) {
                        IUser user = userManagerService.getUserByName(userName);
                        List<? extends IRole> roles = userManagerService.getAllRolesOfUser(user.getId());
                        List<String> roleList = roles.stream().map(IRole::getId).collect(Collectors.toList());
                        if (!roleList.contains(userManagerService.getRoleByName(defaultRole).getId())) {
                            IRole role = userManagerService.getRoleByName(defaultRole);
                            roleList.add(role.getId());
                            userManagerService.assignRolesToUser(user.getId(), roleList);
                        }

                        logger.info("用户{}在BI系统中已经存在", userName);
                    } else {
                        String ret = userManagerService.createUser(glamDep.getId(), userName, "", "", defaultPassword, true);
                        List<String> roles = new ArrayList<String>();
                        if ("ops".equals(userName)) {
                            IRole role = userManagerService.getRoleByName("glscdata");
                            roles.add(role.getId());
                        }
                        IRole role = userManagerService.getRoleByName(defaultRole);
                        roles.add(role.getId());
                        boolean rolesToUser = userManagerService.assignRolesToUser(ret, roles);
                        logger.info("用户{}在BI系统创建成功,id为{}", userName, ret);
                    }
                } catch (Exception e) {
                    logger.error("用户{}创建失败,原因:{}", userName, e);
                }

            }
        } catch (Exception e) {
            logger.error("BI用户创建失败", e);
        } finally {
            conn.close();
        }
        return userName;
    }


    public List<String> createUsers(List<String> userNames) {

        logger.info("开始新增{}个用户信息至[{}]-{}组-{}角色", userNames.size(), smartbi_url, defaultDepartmentName, defaultRole);
        if (userNames.size() == 0) {
            return userNames;
        }


        List<String> fails = new ArrayList<String>();
        fails.addAll(userNames);

        ClientConnector conn = new ClientConnector(smartbi_url);
        try {
            logger.info("开始连接BI,用户名:{},密码{}", smartbi_username, smartbi_password);
            boolean con = conn.open(smartbi_username, smartbi_password);
            if (con) {
                logger.info("连接BI成功");
                UserManagerService userManagerService = new UserManagerService(conn);
                //{"departmentCode":"glam","name":"glam","alias":"glam","id":"Iff8080810176dfe6dfe65fb90176efdd74621699","desc":""}
                IDepartment glamDep = userManagerService.getDepartmentByName(defaultDepartmentName);
                if (glamDep == null) {
                    logger.error("所有BI用户创建失败,组{}不存在", defaultDepartmentName);
                }

                //key:username value:中文名

                Map<String, String> biUserMap = userManagerService.getAllUsers()
                        .stream().collect(Collectors.toMap(IUser::getName, IUser::getAlias));

                Iterator<String> it = fails.iterator();
                System.out.println(biUserMap.toString());
                while (it.hasNext()) {
                    String userName = it.next();
                    try {
                        //已经存在
                        if (biUserMap.containsKey(userName)) {
                            it.remove();
                            IUser user = userManagerService.getUserByName(userName);
                            List<? extends IRole> roles = userManagerService.getAllRolesOfUser(user.getId());
                            List<String> roleList = roles.stream().map(IRole::getId).collect(Collectors.toList());
                            if (!roleList.contains(userManagerService.getRoleByName(defaultRole).getId())) {
                                IRole role = userManagerService.getRoleByName(defaultRole);
                                roleList.add(role.getId());
                                userManagerService.assignRolesToUser(user.getId(), roleList);
                            }

                            logger.info("用户{}在BI系统中已经存在", userName);
                            continue;
                        } else {
                            String ret = userManagerService.createUser(glamDep.getId(), userName, "", "", defaultPassword, true);
                            List<String> roles = new ArrayList<String>();
                            if ("ops".equals(userName)) {
                                IRole role = userManagerService.getRoleByName("glscdata");
                                roles.add(role.getId());
                            }
                            IRole role = userManagerService.getRoleByName(defaultRole);
                            roles.add(role.getId());
                            boolean rolesToUser = userManagerService.assignRolesToUser(ret, roles);
                            it.remove();
                            logger.info("用户{}在BI系统创建成功,id为{}", userName, ret);
                        }
                    } catch (Exception e) {
                        logger.error("用户{}创建失败,原因:{}", userName, e);
                    }
                }
            }
        } catch (Exception e) {
            logger.error("BI用户创建失败", e);
        } finally {
            conn.close();
        }

        return fails;
    }

    public List<String> deleteUsers(List<String> userNames) {

        logger.info("开始删除{}个用户信息至[{}]-{}组-{}角色", userNames.size(), smartbi_url, defaultDepartmentName, defaultRole);
        if (userNames.size() == 0) {
            return userNames;
        }

        List<String> fails = new ArrayList<String>();
        fails.addAll(userNames);

        ClientConnector conn = new ClientConnector(smartbi_url);
        try {
            boolean con = conn.open(smartbi_username, smartbi_password);
            if (con) {
                UserManagerService userManagerService = new UserManagerService(conn);
                //{"departmentCode":"glam","name":"glam","alias":"glam","id":"Iff8080810176dfe6dfe65fb90176efdd74621699","desc":""}
                IDepartment glamDep = userManagerService.getDepartmentByName(defaultDepartmentName);
                if (glamDep == null) {
                    logger.error("所有BI用户删除失败,组{}不存在", defaultDepartmentName);
                }

                //key:username value:id
                Map<String, String> biDepUserMap = userManagerService.getUsersOfDepartment(glamDep.getId())
                        .stream().collect(Collectors.toMap(IUser::getName, IUser::getId));

                Map<String, String> biUserMap = userManagerService.getAllUsers()
                        .stream().collect(Collectors.toMap(IUser::getName, IUser::getId));

                Iterator<String> it = fails.iterator();
                while (it.hasNext()) {
                    String userName = it.next();
                    try {
                        //不存在，不用删除
                        if (!biUserMap.containsKey(userName)) {
                            it.remove();
                            logger.info("用户{}在BI系统中不存在", userName);
                            continue;
                        } else {
                            if (!biDepUserMap.containsKey(userName)) {
                                it.remove();
                                IUser user = userManagerService.getUserByName(userName);
                                List<? extends IRole> roles = userManagerService.getAllRolesOfUser(user.getId());
                                List<String> roleList = roles.stream().map(IRole::getId).collect(Collectors.toList());
                                if (roleList.contains(userManagerService.getRoleByName(defaultRole).getId())) {
                                    IRole role = userManagerService.getRoleByName(defaultRole);
                                    roleList.remove(role.getId());
                                    userManagerService.assignRolesToUser(user.getId(), roleList);
                                }

                                logger.info("用户{}在BI系统中已经存在", userName);
                                continue;
                            } else {
                                boolean success = userManagerService.deleteUser(biUserMap.get(userName));
                                if (success) {
                                    it.remove();
                                    logger.info("用户{}在BI系统删除成功", userName);
                                } else {
                                    logger.info("用户{}在BI系统删除失败", userName);
                                }
                            }

                        }
                    } catch (Exception e) {
                        logger.error("用户{}删除失败,原因:{}", userName, e);
                    }
                }
            }
        } catch (Exception e) {
            logger.error("BI用户删除失败", e);
        } finally {
            conn.close();
        }

        return fails;
    }


    public BiExportDto doExport(String reportId, String fileType, String fileName, Map<String, String> param, String biVersion) {
        String biUrl = null;
        String biUserName = null;
        String biPassword = null;
        if(biVersion == null || biVersion.equalsIgnoreCase(SmartBiVersionConstants.V9.getValue())) {
            biUrl = smartbi_url;
            biUserName = smartbi_username;
            biPassword = smartbi_password;
            biVersion = SmartBiVersionConstants.V9.getValue();
        }
        else if(biVersion.equalsIgnoreCase(SmartBiVersionConstants.V10.getValue())) {
            biUrl = smartbiv10_url;
            biUserName = smartbiv10_username;
            biPassword = smartbiv10_password;
        }
        if(Objects.isNull(biUrl)) {
            logger.error("unsupport BI version: {}", biVersion);
            return null;
        }

        ClientConnector conn = new ClientConnector(biUrl);
        try {
            boolean con = conn.open(biUserName, biPassword);
            if (con) {
                SSReport report = new SSReport(conn);
                BiExportDto biExportDto = new BiExportDto();
                biExportDto.setFileName(fileName);
                biExportDto.setFileType(fileType);
                biExportDto.setParams(param);
                biExportDto.setReportId(reportId);
                report.open(reportId);
                // 多数据集共用参数，通过接口获取参数id再设置参数
                List<Parameter> paramList = report.getParamList();
                for (int i = 0; i < paramList.size(); i++) {
                    String name = paramList.get(i).getName();    //参数名称
                    String id = paramList.get(i).getId();    //参数id
                    // 不设置未传参数，使用默认参数
                    String value = param.get(name);
                    if (StringUtils.hasText(value)) {
                        report.setParamValue(id, value, name);
                    }
                }
                logger.info("报表为{}参数为{}", reportId, report.getParamList());
                FileOutputStream fileOutputStream = null;
                String uuid = UUID.randomUUID().toString().replaceAll("-", "");
                String myPath = nasPath + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + "/" + uuid;
                biExportDto.setUuid(uuid);
                Path path = Paths.get(myPath);
                if (!Files.exists(path)) {
                    try {
                        Files.createDirectories(path);
                    } catch (IOException e) {
                        logger.error("创建文件夹{}失败", myPath);
                    }
                }
                myPath = myPath + File.separator;
                if ("xlsx".equals(fileType)) {
                    myPath = myPath + fileName + ".xlsx";
                    fileOutputStream = new FileOutputStream(new File(myPath));
                    report.doExport("EXCEL2007", fileOutputStream);    //大写格式
                    fileOutputStream.flush();
                    fileOutputStream.close();
                    biExportDto.setFilePath(myPath);
                    return biExportDto;
                }

                if ("xls".equals(fileType)) {
                    myPath = myPath + fileName + ".xls";
                    fileOutputStream = new FileOutputStream(new File(myPath));
                    report.doExport("EXCEL", fileOutputStream);    //大写格式
                    fileOutputStream.flush();
                    fileOutputStream.close();
                    biExportDto.setFilePath(myPath);
                    return biExportDto;
                }

                if ("pdf".equals(fileType)) {
                    myPath = myPath + fileName + ".pdf";
                    fileOutputStream = new FileOutputStream(new File(myPath));
                    report.doExport("PDF", fileOutputStream);    //大写格式
                    fileOutputStream.flush();
                    fileOutputStream.close();
                    biExportDto.setFilePath(myPath);
                    return biExportDto;
                }

                if ("png".equals(fileType)) {
                    myPath = myPath + fileName + ".png";
                    fileOutputStream = new FileOutputStream(new File(myPath));
                    report.doExport("PNG", fileOutputStream);    //大写格式
                    fileOutputStream.flush();
                    fileOutputStream.close();
                    biExportDto.setFilePath(myPath);
                    return biExportDto;
                }

                if ("docx".equals(fileType)) {
                    myPath = myPath + fileName + ".docx";
                    fileOutputStream = new FileOutputStream(new File(myPath));
                    report.doExport("WORD", fileOutputStream);    //大写格式
                    fileOutputStream.flush();
                    fileOutputStream.close();
                    biExportDto.setFilePath(myPath);
                    return biExportDto;
                }

            } else {
                logger.error("登录导出报表失败");
            }
        } catch (Exception e) {
            logger.error("导出报表失败,原因:{}", e);
        } finally {
            conn.close();
        }
        return null;
    }

    /**
     * @author: qiyr
     * @date: 2022/6/6 13:15
     * @description: 无参导出（默认参数）
     */
    public BiExportDto doExport(String reportId, String fileType, String fileName,String biVersion) {
        String biUrl = null;
        String biUserName = null;
        String biPassword = null;
        if(biVersion == null || biVersion.equalsIgnoreCase(SmartBiVersionConstants.V9.getValue())) {
            biUrl = smartbi_url;
            biUserName = smartbi_username;
            biPassword = smartbi_password;
            biVersion = SmartBiVersionConstants.V9.getValue();
        }
        else if(biVersion.equalsIgnoreCase(SmartBiVersionConstants.V10.getValue())) {
            biUrl = smartbiv10_url;
            biUserName = smartbiv10_username;
            biPassword = smartbiv10_password;
        }
        if(Objects.isNull(biUrl)) {
            logger.error("unsupport BI version: {}", biVersion);
            return null;
        }

        ClientConnector conn = new ClientConnector(biUrl);
        try {
            boolean con = conn.open(biUserName, biPassword);
            if (con) {
                SSReport report = new SSReport(conn);
                BiExportDto biExportDto = new BiExportDto();
                biExportDto.setFileName(fileName);
                biExportDto.setFileType(fileType);
                biExportDto.setReportId(reportId);
                report.open(reportId);
                // 多数据集共用参数，通过接口获取参数id再设置参数
                List<Parameter> paramList = report.getParamList();
                for (Parameter parameter : paramList) {
                    System.out.println(parameter);
                    report.setParamValue(parameter.getId(), parameter.getValue(), parameter.getDisplayValue());
                }
                // 设置默认参数
                biExportDto.setParams(paramList.stream().collect(Collectors.toMap(Parameter::getName, Parameter::getValue)));
                logger.info("报表为{}默认参数为{}", reportId, report.getParamList());
                FileOutputStream fileOutputStream = null;
                String uuid = UUID.randomUUID().toString().replaceAll("-", "");
                String myPath = nasPath + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + "/" + uuid;
                biExportDto.setUuid(uuid);
                Path path = Paths.get(myPath);
                if (!Files.exists(path)) {
                    try {
                        Files.createDirectories(path);
                    } catch (IOException e) {
                        logger.error("创建文件夹{}失败", myPath);
                    }
                }
                myPath = myPath + File.separator;
                if ("xlsx".equals(fileType)) {
                    myPath = myPath + fileName + ".xlsx";
                    fileOutputStream = new FileOutputStream(new File(myPath));
                    report.doExport("EXCEL2007", fileOutputStream);    //大写格式
                    fileOutputStream.flush();
                    fileOutputStream.close();
                    biExportDto.setFilePath(myPath);
                    return biExportDto;
                }

                if ("xls".equals(fileType)) {
                    myPath = myPath + fileName + ".xls";
                    fileOutputStream = new FileOutputStream(new File(myPath));
                    report.doExport("EXCEL", fileOutputStream);    //大写格式
                    fileOutputStream.flush();
                    fileOutputStream.close();
                    biExportDto.setFilePath(myPath);
                    return biExportDto;
                }

                if ("pdf".equals(fileType)) {
                    myPath = myPath + fileName + ".pdf";
                    fileOutputStream = new FileOutputStream(new File(myPath));
                    report.doExport("PDF", fileOutputStream);    //大写格式
                    fileOutputStream.flush();
                    fileOutputStream.close();
                    biExportDto.setFilePath(myPath);
                    return biExportDto;
                }

                if ("png".equals(fileType)) {
                    myPath = myPath + fileName + ".png";
                    fileOutputStream = new FileOutputStream(new File(myPath));
                    report.doExport("PNG", fileOutputStream);    //大写格式
                    fileOutputStream.flush();
                    fileOutputStream.close();
                    biExportDto.setFilePath(myPath);
                    return biExportDto;
                }

                if ("docx".equals(fileType)) {
                    myPath = myPath + fileName + ".docx";
                    fileOutputStream = new FileOutputStream(new File(myPath));
                    report.doExport("WORD", fileOutputStream);    //大写格式
                    fileOutputStream.flush();
                    fileOutputStream.close();
                    biExportDto.setFilePath(myPath);
                    return biExportDto;
                }

            } else {
                logger.error("登录导出报表失败");
            }
        } catch (Exception e) {
            logger.error("导出报表失败,原因:{}", e);
        } finally {
            conn.close();
        }
        return null;
    }


    // --------------------------------------------------------------------------------------------------
    // 返回加密后的字符串
    // key是用于生成密钥的字符串，input是要加密的字符串
    private String getEncryptedString(String input, String key) throws InvalidKeyException, Exception {
        Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, getKey(key));
        byte[] inputBytes = input.getBytes(StandardCharsets.UTF_8);
        byte[] outputBytes = cipher.doFinal(inputBytes);
        Base64.Encoder encoder = Base64.getEncoder();
        String base64 = encoder.encodeToString(outputBytes);
        return base64;
    }

    private SecretKey getKey(String s) throws Exception {
        SecretKeyFactory kf = SecretKeyFactory.getInstance("DES");
        DESKeySpec ks = new DESKeySpec(s.getBytes());
        SecretKey kd = kf.generateSecret(ks);
        return kd;
    }


    // --------------------------------------------------------------------------------------------------
    // 返回解密后的字符串
    // key是用于生成密钥的字符串，input是要解密的字符串
    private String getDecryptedString(String input, String key) throws Exception {
        String result = null;
        Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, getKey(key));
        Base64.Decoder decoder = Base64.getDecoder();
        byte[] raw = decoder.decode(input);
        byte[] stringBytes = cipher.doFinal(raw);
        result = new String(stringBytes, StandardCharsets.UTF_8);
        return result;
    }


    //解密
    private String getDecryptedString2(String input) {
        try {
            return getDecryptedString(URLDecoder.decode(input, "UTF-8"), smartbi_key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


}
